﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text.RegularExpressions;

namespace slCommunicator
{
    public static class Listener
    {
        public static int AssignedPort = Properties.Settings.Default.workingPort;
        private static TcpListener listener;

        public static void CreateStart()
        {
            Console.WriteLine(localization.listener_startRequested);
            listener = new TcpListener(IPAddress.Any, AssignedPort);
            listener.AllowNatTraversal(true);
            listener.Start();
            AssignedPort = (listener.LocalEndpoint as IPEndPoint).Port;
            SetAccept();
            Console.WriteLine(localization.listener_startCompleted, AssignedPort);
        }

        private static void SetAccept()
        {
            listener.BeginAcceptTcpClient(new AsyncCallback(AcceptClients), null);
        }

        private static void AcceptClients(IAsyncResult ar)
        {
            TcpClient newClient = listener.EndAcceptTcpClient(ar);
            SetAccept();
            Console.WriteLine(localization.listener_newConnection, newClient.Client.RemoteEndPoint);
            new Session(newClient, true);
        }
    }

    public struct CallResult
    {
        public bool bSuccess;
        public TcpClient client;
        public Exception error;
    }

    public static class CallStarter
    {
        public static CallResult Call(IPEndPoint target)
        {
            CallResult result = new CallResult();
            Console.WriteLine(localization.caller_newCallTry,target);
            try
            {
                var client = new TcpClient();
                var asyncresult = client.BeginConnect(target.Address, target.Port, null, null);

                var success = asyncresult.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(Properties.Settings.Default.tcpCallConnectTimeoutMs));

                if (!success)
                {
                    throw new TimeoutException(localization.caller_timeoutException);
                }

                client.EndConnect(asyncresult);
                result.bSuccess = success;
                result.client = client;
                Console.WriteLine(localization.caller_succeeded, target, client.Client.LocalEndPoint);
                new Session(client, false);
            }
            catch (Exception exp)
            {
                result.error = exp;
                result.bSuccess = false;
                result.client = null;
                Console.WriteLine(localization.caller_Exception, target, exp.GetType(), exp.Source, exp.Message);
            }
            finally {  }

            return result;
        }
    }

    public static class Register
    {
        public static IPAddress FindPublicIP()
        {
            IPAddress found = IPAddress.Loopback;
            try
            {
                HttpWebRequest req = (HttpWebRequest)HttpWebRequest.CreateHttp(localization.register_findpubipUrl);
                req.Method = "GET";

                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                var r = new StreamReader(resp.GetResponseStream());
                string all = r.ReadToEnd();
                var caps = Regex.Match(all, @"(?:\d{1,3}\.){3}\d{1,3}").Captures;

                foreach (Capture cap in caps)
                {
                    IPAddress test;
                    if (IPAddress.TryParse(cap.Value, out test))
                        found = test;
                }
            }
            catch (Exception exp)
            {
                Console.WriteLine(localization.register_ipfinderror, exp.GetType() + " " + exp.Message);
            }

            return found;
        }

        public static void RegisterMe()
        {
            if (!Properties.Settings.Default.doRegisterPeer)
            {
                Console.WriteLine(localization.register_skip);
                return;
            }
            RegisterMe(new IPEndPoint(FindPublicIP(), Listener.AssignedPort));
        }
        public static void RegisterMe(IPEndPoint ipe)
        {
            if (!Properties.Settings.Default.doRegisterPeer)
            {
                Console.WriteLine(localization.register_skip);
                return;
            }

            Console.WriteLine(localization.register_begin);
            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.CreateHttp(localization.register_webserviceurl);
            req.Method = "POST";

            var sw = new StreamWriter(req.GetRequestStream());
            sw.WriteLine(ipe.Address);
            sw.WriteLine(ipe.Port);
            sw.WriteLine(DateTime.Now.ToBinary());
            sw.WriteLine(ContactInformation.MyContactInfo.DisplayName);
            sw.Close();

            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            var r = new StreamReader(resp.GetResponseStream());
            string all = r.ReadToEnd();
            r.Close();
            Console.WriteLine(localization.register_result, all);
        }

        public static void PrintTodayJoinLog()
        {
            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.CreateHttp(new Uri(new Uri(localization.register_webserviceurl),
                "JoinLog/" + DateTime.Now.ToString("dd-MM-yy") + ".txt"));
            req.Method = "GET";

            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            var r = new StreamReader(resp.GetResponseStream());
            while (!r.EndOfStream)
            {
                string line = r.ReadLine();
                string[] args = line.Split(' ');

                IPAddress ip; int port; long time;
                if (IPAddress.TryParse(args[0], out ip) && int.TryParse(args[1], out port) && long.TryParse(args[2], out time) && !string.IsNullOrEmpty(args[3]))
                {
                    Console.WriteLine("{0:t} {1} {2} {3}", DateTime.FromBinary(time), ip, port, 
                        Encoding.UTF8.GetString(Convert.FromBase64String(args[3])));
                }
            }
        }
    }
}
